﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace ChatClient.Utility
{
    class MessageStream
    {
        private TcpClient _tcpClient;
        private bool _running;
        private Queue<string> _messages;
        private StreamReader _reader;
        private StreamWriter _writer;

        private readonly ConnectionWorker _connectionWorker;

        public delegate void MessageResponseDelegate(string message);
        public event MessageResponseDelegate HandleResponse;

        public MessageStream()
        {
            _connectionWorker = new ConnectionWorker(this);
        }

        public void Start()
        {
            if (_running) return;

            System.Diagnostics.Debug.WriteLine("Starting messages stream...");
            _messages = new Queue<string>();

            try
            {
                _tcpClient = new TcpClient(Properties.Settings.Default.IPAddress, Properties.Settings.Default.PortNumber);
                _reader = new StreamReader(_tcpClient.GetStream());
                _writer = new StreamWriter(_tcpClient.GetStream());

                _connectionWorker.RunWorkerAsync();
            }
            catch (SocketException e)
            {
                System.Diagnostics.Debug.WriteLine("Start error: " + e);
                MessageBox.Show(@"Error: Cannot connect to server");
            }
        }

        public void SendMessage(string message)
        {
            lock (_messages)
            {
                _messages.Enqueue(message);
            }
        }

        public void Close()
        {
            if (!_running) return;

            _writer.WriteLine(("END"));
            _writer.Flush();
            _connectionWorker.CancelAsync();
            _tcpClient.Close();

            _running = false;
        }

        private class  ConnectionWorker : BackgroundWorker
        {
            private readonly MessageStream _outer;

            public ConnectionWorker(MessageStream outer)
            {
                _outer = outer;
                WorkerReportsProgress = false;
                WorkerSupportsCancellation = true;
            }

            protected override void OnDoWork(DoWorkEventArgs e)
            {
                _outer._running = true;
                System.Diagnostics.Debug.WriteLine("Connection loop running....");

                try
                {
                    while (_outer._running && !CancellationPending)
                    {
                        Application.DoEvents();
                        lock (_outer._messages)
                        {
                            System.Diagnostics.Debug.WriteLine("Message queue size: " + _outer._messages.Count);

                            if (_outer._messages.Count == 0)
                            {
                                System.Diagnostics.Debug.WriteLine("No messages to send....");
                                Thread.Sleep(100);
                            }
                            else
                            {
                                var message = _outer._messages.Dequeue();
                                System.Diagnostics.Debug.WriteLine("Sending: " + Rot13EncryptDecrypt.Transform(message));
                                _outer._writer.WriteLine(Rot13EncryptDecrypt.Transform(message));
                                _outer._writer.Flush();

                                System.Diagnostics.Debug.WriteLine("Waiting for response...");
                                var response = _outer._reader.ReadLine();

                                System.Diagnostics.Debug.WriteLine("Got response: " + response);
                                _outer.HandleResponse(Rot13EncryptDecrypt.Transform(response));
                            }
                        }
                    }
                } catch (Exception error)
                {
                    System.Diagnostics.Debug.WriteLine("Connection loop terminated" + error);
                    _outer.Close();
                }
            }
        }
    }
}
